/*
 * Copyright (C) 2005-2007 Alfresco Software Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

 * As a special exception to the terms and conditions of version 2.0 of 
 * the GPL, you may redistribute this Program in connection with Free/Libre 
 * and Open Source Software ("FLOSS") applications as described in Alfresco's 
 * FLOSS exception.  You should have recieved a copy of the text describing 
 * the FLOSS exception, and it is also available here: 
 * http://www.alfresco.com/legal/licensing"
 */
package fr.umlv.utils;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;


// TODO: Auto-generated Javadoc
/**
 * The Class ISO8601DateFormat.
 */
public class ISO8601DateFormat
{

	/**
	 * Format.
	 *
	 * @param isoDate the iso date
	 * @return the string
	 */
	public static String format(Date isoDate)
	{
		// Note: always serialise to Gregorian Calendar
		Calendar calendar = new GregorianCalendar();
		calendar.setTime(isoDate);

		StringBuffer formatted = new StringBuffer();
		padInt(formatted, calendar.get(Calendar.YEAR), 4);
		formatted.append('-');
		padInt(formatted, calendar.get(Calendar.MONTH) + 1, 2);
		formatted.append('-');
		padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), 2);
		formatted.append('T');
		padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), 2);
		formatted.append(':');
		padInt(formatted, calendar.get(Calendar.MINUTE), 2);
		formatted.append(':');
		padInt(formatted, calendar.get(Calendar.SECOND), 2);
		formatted.append('.');
		padInt(formatted, calendar.get(Calendar.MILLISECOND), 3);

		TimeZone tz = calendar.getTimeZone();
		int offset = tz.getOffset(calendar.getTimeInMillis());
		if (offset != 0)
		{
			int hours = Math.abs((offset / (60 * 1000)) / 60);
			int minutes = Math.abs((offset / (60 * 1000)) % 60);
			formatted.append(offset < 0 ? '-' : '+');
			padInt(formatted, hours, 2);
			formatted.append(':');
			padInt(formatted, minutes, 2);
		} 
		else
		{
			formatted.append('Z');
		}

		return formatted.toString();
	}


	/**
	 * Parses the.
	 *
	 * @param isoDate the iso date
	 * @return the date
	 */
	public static Date parse(String isoDate)
	{
		Date parsed = null;

		int offset = 0;

		// extract year
		int year = Integer.parseInt(isoDate.substring(offset, offset += 4));
		if (isoDate.charAt(offset) != '-')
		{
			throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset));
		}

		// extract month
		int month = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
		if (isoDate.charAt(offset) != '-')
		{
			throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset));
		}

		// extract day
		int day = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
		if (isoDate.charAt(offset) != 'T')
		{
			throw new IndexOutOfBoundsException("Expected T character but found " + isoDate.charAt(offset));
		}

		// extract hours, minutes, seconds and milliseconds
		int hour = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
		if (isoDate.charAt(offset) != ':')
		{
			throw new IndexOutOfBoundsException("Expected : character but found " + isoDate.charAt(offset));
		}
		int minutes = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
		if (isoDate.charAt(offset) != ':')
		{
			throw new IndexOutOfBoundsException("Expected : character but found " + isoDate.charAt(offset));
		}
		int seconds = Integer.parseInt(isoDate.substring(offset += 1 , offset += 2));
		if (isoDate.charAt(offset) != '.')
		{
			throw new IndexOutOfBoundsException("Expected . character but found " + isoDate.charAt(offset));
		}
		int milliseconds = Integer.parseInt(isoDate.substring(offset += 1, offset += 3));

		// extract timezone
		String timezoneId;
		char timezoneIndicator = isoDate.charAt(offset);
		if (timezoneIndicator == '+' || timezoneIndicator == '-')
		{
			timezoneId = "GMT" + isoDate.substring(offset);
		}
		else if (timezoneIndicator == 'Z')
		{
			timezoneId = "GMT";
		}
		else
		{
			throw new IndexOutOfBoundsException("Invalid time zone indicator " + timezoneIndicator);
		}
		TimeZone timezone = TimeZone.getTimeZone(timezoneId);
		if (!timezone.getID().equals(timezoneId))
		{
			throw new IndexOutOfBoundsException();
		}

		// initialize Calendar object#
		// Note: always de-serialise from Gregorian Calendar
		Calendar calendar = new GregorianCalendar(timezone);
		calendar.setLenient(false);
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month - 1);
		calendar.set(Calendar.DAY_OF_MONTH, day);
		calendar.set(Calendar.HOUR_OF_DAY, hour);
		calendar.set(Calendar.MINUTE, minutes);
		calendar.set(Calendar.SECOND, seconds);
		calendar.set(Calendar.MILLISECOND, milliseconds);

		// extract the date
		parsed = calendar.getTime();

		return parsed;
	}

	/**
	 * Pad int.
	 *
	 * @param buffer the buffer
	 * @param value the value
	 * @param length the length
	 */
	private static void padInt(StringBuffer buffer, int value, int length)
	{
		String strValue = Integer.toString(value);
		for (int i = length - strValue.length(); i > 0; i--)
		{
			buffer.append('0');
		}
		buffer.append(strValue);
	}

}
